home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
b
/
b.lha
/
B
/
src
/
bed
/
deco.c
< prev
next >
Wrap
C/C++ Source or Header
|
1988-11-24
|
13KB
|
679 lines
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1984. */
static char rcsid[] = "$Header: deco.c,v 2.3 84/07/19 11:45:12 guido Exp $";
/*
* B editor -- Delete and copy commands.
*/
#include <ctype.h>
#include "b.h"
#include "erro.h"
#include "bobj.h"
#include "node.h"
#include "gram.h"
#include "supr.h"
#include "queu.h"
value copyout(); /* Forward */
/*
* DELETE and COPY currently share a buffer, called the copy buffer.
* (Physically, there is one such a buffer in each environment.)
* In ordinary use, the copy buffer receives the text deleted by the
* last DELETE command (unless it just removed a hole); the COPY command
* can then be used (with the focus on a hole) to copy it back.
* When some portion of text must be held while other text is deleted,
* the COPY command again, but now with the focus on the text to be held,
* copies it to the buffer and deleted text won't overwrite the buffer
* until it is copied back at least once.
* If the buffer holds text that was explicitly copied out but not yet
* copied back in, it is saved on a file when the editor exits, so it can
* be used in the next session; but this is not true for text implicitly
* placed in the buffer through DELETE.
*/
/*
* Delete command -- delete the text in the focus, or delete the hole
* if it is only a hole.
*/
Visible bool
delete(ep)
register environ *ep;
{
higher(ep);
shrink(ep);
if (ishole(ep))
return delhole(ep);
if (!ep->copyflag) {
release(ep->copybuffer);
ep->copybuffer = copyout(ep);
}
return delbody(ep);
}
/*
* Delete the focus under the assumption that it contains some text.
*/
Visible bool
delbody(ep)
register environ *ep;
{
ep->changed = Yes;
subgrow(ep, No); /* Don't ignore spaces */
switch (ep->mode) {
case SUBRANGE:
if (ep->s1&1)
return delfixed(ep);
return delvarying(ep);
case SUBSET:
return delsubset(ep, Yes);
case SUBLIST:
return delsublist(ep);
case WHOLE:
return delwhole(ep);
default:
Abort();
/* NOTREACHED */
}
}
/*
* Delete portion (ep->mode == SUBRANGE) of varying text ((ep->s1&1) == 0).
*/
Hidden bool
delvarying(ep)
register environ *ep;
{
auto queue q = Qnil;
register node n = tree(ep->focus);
auto value v = (value) child(n, ep->s1/2);
register len = Length(v);
Assert(ep->mode == SUBRANGE && !(ep->s1&1)); /* Wrong call */
Assert(Type(v) == Tex); /* Inconsistent parse tree */
if (ep->s2 == 0
&& !mayinsert(tree(ep->focus), ep->s1/2, 0, Str(v)[ep->s3 + 1])) {
/* Cannot do simple substring deletion. */
stringtoqueue(Str(v) + ep->s3 + 1, &q);
delfocus(&ep->focus);
ep->mode = WHOLE;
return app_queue(ep, &q);
}
v = copy(v);
putintrim(&v, ep->s2, len - ep->s3 - 1, "");
s_downi(ep, ep->s1/2);
replace(&ep->focus, (node) v);
s_up(ep);
ep->mode = VHOLE;
return Yes;
}
/*
* Delete portion (ep->mode == SUBRANGE) of fixed text ((ep->s1&1) == 1).
*/
Hidden bool
delfixed(ep)
register environ *ep;
{
register node n = tree(ep->focus);
char buf[15]; /* Long enough for all fixed texts */
register string repr = noderepr(n)[ep->s1/2];
register int len;
queue q = Qnil;
bool ok;
Assert(ep->mode == SUBRANGE && (ep->s1&1));
if (ep->s1 > 1) {
ep->mode = FHOLE;
return Yes;
}
Assert(fwidth(repr) < sizeof buf - 1);
len = ep->s2;
ep->s2 = ep->s3 + 1;
ep->mode = FHOLE;
nosuggtoqueue(ep, &q);
strcpy(buf, repr);
if (nchildren(tree(ep->focus)) > 0)
buf[len] = 0;
else
strcpy(buf+len, buf+ep->s2);
delfocus(&ep->focus);
ep->mode = WHOLE;
markpath(&ep->focus, 1);
ok = ins_string(ep, buf, &q, 0);
if (!ok) {
qrelease(q);
return No;
}
firstmarked(&ep->focus, 1) || Abort();
unmkpath(&ep->focus, 1);
fixfocus(ep, len);
return app_queue(ep, &q);
}
/*
* Delete focus if ep->mode == SUBSET.
*/
Hidden bool
delsubset(ep, hack)
register environ *ep;
bool hack;
{
auto queue q = Qnil;
auto queue q2 = Qnil;
register node n = tree(ep->focus);
register node nn;
register string *rp = noderepr(n);
register int nch = nchildren(n);
register int i;
if (hack) {
shrsubset(ep);
if (ep->s1 == ep->s2 && !(ep->s1&1)) {
nn = child(tree(ep->focus), ep->s1/2);
if (fwidth(noderepr(nn)[0]) < 0) {
/* It starts with a newline, leave the newline */
s_downi(ep, ep->s1/2);
ep->mode = SUBSET;
ep->s1 = 2;
ep->s2 = 2*nchildren(nn) + 1;
return delsubset(ep, hack);
}
}
subgrsubset(ep, No); /* Undo shrsubset */
if (ep->s2 == 3 && rp[1] && Strequ(rp[1], "\t"))
--ep->s2; /* Hack for deletion of unit-head or if/for/wh. head */
}
if (ep->s1 == 1 && Fw_negative(rp[0]))
++ep->s1; /* Hack for deletion of test-suite or refinement head */
if (Fw_zero(rp[0]) ? (ep->s2 < 3 || ep->s1 > 3) : ep->s1 > 1) {
/* No deep structural change */
for (i = (ep->s1+1)/2; i <= ep->s2/2; ++i) {
s_downi(ep, i);
delfocus(&ep->focus);
s_up(ep);
}
if (ep->s1&1) {
ep->mode = FHOLE;
ep->s2 = 0;
}
else if (Type(child(tree(ep->focus), ep->s1/2)) == Tex) {
ep->mode = VHOLE;
ep->s2 = 0;
}
else {
s_downi(ep, ep->s1/2);
ep->mode = ATBEGIN;
}
return Yes;
}
balance(ep); /* Make balanced \t - \b pairs */
subsettoqueue(n, 1, ep->s1-1, &q);
subsettoqueue(n, ep->s2+1, 2*nch+1, &q2);
nonewline(&q2); /* Wonder what will happen...? */
delfocus(&ep->focus);
ep->mode = ATBEGIN;
leftvhole(ep);
if (!ins_queue(ep, &q, &q2)) {
qrelease(q2);
return No;
}
return app_queue(ep, &q2);
}
/*
* Delete the focus if ep->mode == SUBLIST.
*/
delsublist(ep)
register environ *ep;
{
register node n;
register int i;
register int sym;
queue q = Qnil;
bool flag;
Assert(ep->mode == SUBLIST);
n = tree(ep->focus);
flag = fwidth(noderepr(n)[0]) < 0;
for (i = ep->s3; i > 0; --i) {
n = lastchild(n);
Assert(n);
}
if (flag) {
n = nodecopy(n);
s_down(ep);
do {
delfocus(&ep->focus);
} while (rite(&ep->focus));
if (!allowed(ep->focus, symbol(n))) {
error(DEL_REM); /* The remains wouldn't fit */
noderelease(n);
return No;
}
replace(&ep->focus, n);
s_up(ep);
s_down(ep); /* I.e., to leftmost sibling */
ep->mode = WHOLE;
return Yes;
}
sym = symbol(n);
if (sym == Optional || sym == Hole) {
delfocus(&ep->focus);
ep->mode = WHOLE;
}
else if (!allowed(ep->focus, sym)) {
preptoqueue(n, &q);
delfocus(&ep->focus);
ep->mode = WHOLE;
return app_queue(ep, &q);
}
else {
replace(&ep->focus, nodecopy(n));
ep->mode = ATBEGIN;
}
return Yes;
}
/*
* Delete the focus if ep->mode == WHOLE.
*/
Hidden bool
delwhole(ep)
register environ *ep;
{
register int sym = symbol(tree(ep->focus));
Assert(ep->mode == WHOLE);
if (sym == Optional || sym == Hole)
return No;
delfocus(&ep->focus);
return Yes;
}
/*
* Delete the focus if it is only a hole.
* Assume shrink() has been called before!
*/
Hidden bool
delhole(ep)
register environ *ep;
{
node n;
int sym;
bool flag = No;
switch (ep->mode) {
case ATBEGIN:
case VHOLE:
case FHOLE:
case ATEND:
return widen(ep);
case WHOLE:
Assert((sym = symbol(tree(ep->focus))) == Optional || sym == Hole);
if (ichild(ep->focus) != 1)
break;
if (!up(&ep->focus))
return No;
higher(ep);
ep->mode = SUBSET;
ep->s1 = 2;
ep->s2 = 2;
if (fwidth(noderepr(tree(ep->focus))[0]) < 0) {
flag = Yes;
ep->s2 = 3; /* Extend to rest of line */
}
}
ep->changed = Yes;
grow(ep);
switch (ep->mode) {
case SUBSET:
if (!delsubset(ep, No))
return No;
if (!flag)
return widen(ep);
leftvhole(ep);
oneline(ep);
return Yes;
case SUBLIST:
n = tree(ep->focus);
n = lastchild(n);
sym = symbol(n);
if (!allowed(ep->focus, sym)) {
error(DEL_REM); /* The remains wouldn't fit */
return No;
}
flag = samelevel(sym, symbol(tree(ep->focus)));
replace(&ep->focus, nodecopy(n));
if (flag) {
ep->mode = SUBLIST;
ep->s3 = 1;
}
else
ep->mode = WHOLE;
return Yes;
case WHOLE:
Assert(!parent(ep->focus)); /* Must be at root! */
return No;
default:
Abort();
/* NOTREACHED */
}
}
/*
* Subroutine to delete the focus.
*/
Visible Procedure
delfocus(pp)
register path *pp;
{
register path pa = parent(*pp);
register int sympa = pa ? symbol(tree(pa)) : Rootsymbol;
replace(pp, child(gram(sympa), ichild(*pp)));
}
/*
* Copy command -- copy the focus to the copy buffer if it contains
* some text, copy the copy buffer into the focus if the focus is
* empty (just a hole).
*/
Visible bool
copyinout(ep)
register environ *ep;
{
shrink(ep);
if (!ishole(ep)) {
release(ep->copybuffer);
ep->copybuffer = copyout(ep);
ep->copyflag = !!ep->copybuffer;
return ep->copyflag;
}
else {
fixit(ep); /* Make sure it looks like a hole now */
if (!copyin(ep, (queue) ep->copybuffer))
return No;
ep->copyflag = No;
return Yes;
}
}
/*
* Copy the focus to the copy buffer.
*/
Visible value
copyout(ep)
register environ *ep;
{
auto queue q = Qnil;
auto path p;
register node n;
register value v;
char buf[15];
register string *rp;
register int i;
switch (ep->mode) {
case WHOLE:
preptoqueue(tree(ep->focus), &q);
break;
case SUBLIST:
p = pathcopy(ep->focus);
for (i = ep->s3; i > 0; --i)
downrite(&p) || Abort();
for (i = ep->s3; i > 0; --i) {
up(&p) || Abort();
n = tree(p);
subsettoqueue(n, 1, 2*nchildren(n) - 1, &q);
}
pathrelease(p);
break;
case SUBSET:
balance(ep);
subsettoqueue(tree(ep->focus), ep->s1, ep->s2, &q);
break;
case SUBRANGE:
Assert(ep->s3 >= ep->s2);
if (ep->s1&1) { /* Fixed text */
Assert(ep->s3 - ep->s2 + 1 < sizeof buf);
rp = noderepr(tree(ep->focus));
Assert(ep->s2 < Fwidth(rp[ep->s1/2]));
strncpy(buf, rp[ep->s1/2] + ep->s2, ep->s3 - ep->s2 + 1);
buf[ep->s3 - ep->s2 + 1] = 0;
stringtoqueue(buf, &q);
}
else { /* Varying text */
v = (value) child(tree(ep->focus), ep->s1/2);
Assert(Type(v) == Tex);
v = trim(v, ep->s2, Length(v) - ep->s3 - 1);
preptoqueue((node)v, &q);
release(v);
}
break;
default:
Abort();
}
nonewline(&q);
return (value)q;
}
/*
* Subroutine to ensure the copy buffer doesn't start with a newline.
*/
Hidden Procedure
nonewline(pq)
register queue *pq;
{
register node n;
register int c;
if (!emptyqueue(*pq)) {
for (;;) {
n = queuebehead(pq);
if (Type(n) == Tex) {
if (Str((value) n)[0] != '\n')
preptoqueue(n, pq);
noderelease(n);
break;
}
else {
c = nodechar(n);
if (c != '\n')
preptoqueue(n, pq);
else
splitnode(n, pq);
noderelease(n);
if (c != '\n')
break;
}
}
}
}
/*
* Refinement for copyout, case SUBSET: make sure that \t is balanced with \b.
* Actually it can only handle the case where a \t is in the subset and the
* matching \b is immediately following.
*/
Hidden Procedure
balance(ep)
environ *ep;
{
string *rp = noderepr(tree(ep->focus));
int i;
int level = 0;
Assert(ep->mode == SUBSET);
for (i = ep->s1/2; i*2 < ep->s2; ++i) {
if (rp[i]) {
if (index(rp[i], '\t'))
++level;
else if (index(rp[i], '\b'))
--level;
}
}
if (level > 0 && i*2 == ep->s2 && rp[i] && index(rp[i], '\b'))
ep->s2 = 2*i + 1;
}
/*
* Copy the copy buffer to the focus.
*/
Hidden bool
copyin(ep, q)
register environ *ep;
/*auto*/ queue q;
{
auto queue q2 = Qnil;
if (!q) {
error(COPY_EMPTY); /* Empty copy buffer */
return No;
}
ep->changed = Yes;
q = qcopy(q);
if (!ins_queue(ep, &q, &q2)) {
qrelease(q2);
return No;
}
return app_queue(ep, &q2);
}
/*
* Find out whether the focus looks like a hole or if it has some real
* text in it.
* Assumes shrink(ep) has already been performed.
*/
Visible bool
ishole(ep)
register environ *ep;
{
register int sym;
switch (ep->mode) {
case ATBEGIN:
case ATEND:
case VHOLE:
case FHOLE:
return Yes;
case SUBLIST:
case SUBRANGE:
return No;
case SUBSET:
return colonhack(ep); /* (Side-effect!) */
case WHOLE:
sym = symbol(tree(ep->focus));
return sym == Optional || sym == Hole;
default:
Abort();
/* NOTREACHED */
}
}
/*
* Amendment to ishole so that it categorizes '?: ?' as a hole.
* This makes deletion of empty refinements / alternative-suites
* easier (Steven).
*/
Hidden bool
colonhack(ep)
environ *ep;
{
node n = tree(ep->focus);
node n1;
string *rp = noderepr(n);
int i;
int sym;
for (i = ep->s1; i <= ep->s2; ++i) {
if (i&1) {
if (!allright(rp[i/2]))
return No;
}
else {
n1 = child(n, i/2);
if (Type(n1) == Tex)
return No;
sym = symbol(n1);
if (sym != Hole && sym != Optional)
return No;
}
}
return Yes;
}
/*
* Refinement for colonhack. Recognize strings that are almost blank
* (i.e. containing only spaces, colons and the allowed control characters).
*/
Hidden bool
allright(repr)
string repr;
{
if (repr) {
for (; *repr; ++repr) {
if (!index(": \t\b\n\r", *repr))
return No;
}
}
return Yes;
}